﻿using System.Diagnostics.CodeAnalysis;
using System.Globalization;
using System.Linq.Dynamic.Core;
using System.Linq.Expressions;
using System.Reflection;
using Mapster;
using Microsoft.AspNetCore.Http;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.DynamicLinq;
using Microsoft.Extensions.Caching.Distributed;
using Microsoft.Extensions.Logging;

namespace Devonline.AspNetCore;

/// <summary>
/// 公共数据处理服务
/// </summary>
/// <typeparam name="TDbContext">数据库上下文</typeparam>
/// <typeparam name="TEntitySet">业务数据类型</typeparam>
/// <typeparam name="TKey">主键类型</typeparam>
public class DataService<TDbContext, TEntitySet, TKey> :
    IDataService<TDbContext, TEntitySet, TKey>
    where TDbContext : DbContext
    where TEntitySet : class, IEntitySet<TKey>, new()
    where TKey : IConvertible
{
    protected readonly ILogger<DataService<TDbContext, TEntitySet, TKey>> _logger;
    protected readonly TDbContext _context;
    protected readonly IDistributedCache _cache;
    protected readonly HttpSetting _httpSetting;
    protected readonly HttpContext _httpContext;
    protected readonly HttpRequest _request;
    protected readonly DbSet<TEntitySet> _dbSet;
    protected readonly Type _type;
    protected readonly string _typeName;
    protected readonly PropertyInfo[] _propertyInfos;
    protected readonly DistributedCacheEntryOptions _defaultCacheEntryOptions;
    /// <summary>
    /// 用户访问序号, 每次访问分配的序号, 用于在日志中标记用户单次访问的过程唯一性, 此值由 HttpContext.TraceIdentifier 提供
    /// </summary>
    protected readonly string _userAccess;

    /// <summary>
    /// 构造方法
    /// </summary>
    /// <param name="logger">日志</param>
    /// <param name="context">数据库上下文</param>
    /// <param name="httpContextAccessor">Http上下文</param>
    /// <param name="cache">缓存</param>
    /// <param name="httpSetting">配置</param>
    public DataService(
        ILogger<DataService<TDbContext, TEntitySet, TKey>> logger,
        TDbContext context,
        IDistributedCache cache,
        IHttpContextAccessor httpContextAccessor,
        HttpSetting httpSetting
        )
    {
        ArgumentNullException.ThrowIfNull(httpContextAccessor.HttpContext);
        _httpSetting = httpSetting;
        _context = context;
        _context.ChangeTracker.AutoDetectChangesEnabled = false;
        _logger = logger;
        _cache = cache;
        _httpContext = httpContextAccessor.HttpContext;
        _request = _httpContext.Request;
        _dbSet = _context.Set<TEntitySet>();
        _type = typeof(TEntitySet);
        _typeName = _type.GetDisplayName();
        UserName = GetUserName();
        _userAccess = $"User {UserName} in access index {_httpContext.TraceIdentifier}";
        UserAccess = _userAccess;
        DataIsolate = httpSetting.DataIsolate;
        DataIsolateId = GetDataIsolateId();
        EnableCache = _httpSetting.Cache is not null;
        TypeName = _typeName;
        _propertyInfos = _type.GetProperties(BindingFlags.Public | BindingFlags.Instance);
        EnableCache = EnableCache && _type.HasAttribute<CacheableAttribute>();
        _defaultCacheEntryOptions = (_httpSetting.Cache is null) ? new DistributedCacheEntryOptions() : new DistributedCacheEntryOptions { AbsoluteExpirationRelativeToNow = TimeSpan.FromSeconds(_httpSetting.Cache.ExpireTime) };
    }

    #region 基础属性和方法, 业务无关属性和方法
    /// <summary>
    /// 超时时间
    /// </summary>
    private int? _timeout { get; set; }
    /// <summary>
    /// 数据库操作超时时间
    /// </summary>
    public virtual int? Timeout { get; protected set; }
    /// <summary>
    /// 当前对象是否启用了缓存
    /// </summary>
    public virtual bool EnableCache { get; }
    /// <summary>
    /// 当前登录的用户编号
    /// </summary>
    public virtual TKey UserId => GetUserId() ?? throw new UnauthorizedAccessException();
    /// <summary>
    /// 当前登录用户的所属组织单位编号
    /// </summary>
    public virtual TKey? GroupId => GetGroupId();
    /// <summary>
    /// 当前登录的用户
    /// </summary>
    public virtual string UserName { get; }
    /// <summary>
    /// 提供一个对外访问的用户日志标识字符串
    /// </summary>
    public virtual string UserAccess { get; }
    /// <summary>
    /// 当前处理的数据对象类型名称
    /// </summary>
    public virtual string TypeName { get; }
    /// <summary>
    /// 数据隔离级别
    /// </summary>
    public virtual DataIsolateLevel DataIsolate { get; protected set; }
    /// <summary>
    /// 数据隔离的数据编号
    /// </summary>
    public virtual TKey? DataIsolateId { get; protected set; }
    /// <summary>
    /// 数据库操作是否自动提交
    /// </summary>
    public virtual bool? AutoSaveChanges { get; protected set; }
    /// <summary>
    /// 从 httpContext 获取用户标识, 用户标识在 User.Claims 中的 type 为 sub; 在 request 中为 userId
    /// 用户尚未登录系统时抛出 UnauthorizedAccessException 异常
    /// </summary>
    /// <returns></returns>
    /// <exception cref="UnauthorizedAccessException">用户尚未认证访问异常</exception>
    public virtual TKey? GetUserId() => _httpContext.GetUserId<TKey>();
    /// <summary>
    /// 从 httpContext 获取用户主要组织单位标识, 用户标识在 User.Claims 中的 type 为 groupId; 在 request 中为 groupId
    /// 用户尚未登录系统时抛出 UnauthorizedAccessException 异常
    /// </summary>
    /// <returns></returns>
    /// <exception cref="UnauthorizedAccessException">用户尚未认证访问异常</exception>
    public virtual TKey? GetGroupId() => _httpContext.GetGroupId<TKey>();
    /// <summary>
    /// 获取数据隔离的数据编号
    /// </summary>
    /// <returns></returns>
    public virtual TKey? GetDataIsolateId() => _httpContext.GetClaimValue<TKey>(CLAIM_TYPE_DATA_ISOLATE_ID);
    /// <summary>
    /// 从 httpContext 获取用户标识, 用户标识在 User.Claims 中的 type 为 userName
    /// </summary>
    /// <returns></returns>
    public virtual string GetUserName() => _httpContext.GetUserName();
    /// <summary>
    /// 从当前 request 中获取参数的值, 取值顺序 Query -> Form -> Header -> Cookie
    /// </summary>
    /// <param name="key"></param>
    /// <returns></returns>
    public virtual T? GetFromRequest<T>(string key) where T : IConvertible => _request.GetRequestOption<T>(key);
    /// <summary>
    /// 获取上下文对象中参数/变量的值, 取值顺序 HttpContext -> Query -> Form -> Header -> Cookie
    /// </summary>
    /// <param name="key"></param>
    /// <returns></returns>
    public virtual T? GetFromHttpContext<T>(string key) where T : IConvertible => _httpContext.GetFromHttpContext<T>(key);
    /// <summary>
    /// 从分布式缓存获取值
    /// </summary>
    /// <typeparam name="TValue">缓存值类型</typeparam>
    /// <param name="key">缓存键</param>
    /// <returns></returns>
    public virtual async Task<TValue?> GetCacheAsync<TValue>(string key) => await _cache.GetValueAsync<TValue>(key);
    /// <summary>
    /// 将值写入分布式缓存, 带预设过期时间
    /// </summary>
    /// <typeparam name="TValue">缓存值类型</typeparam>
    /// <param name="key">缓存键</param>
    /// <param name="value">缓存值</param>
    public virtual async Task SetCacheAsync<TValue>(string key, [DisallowNull] TValue value) => await _cache.SetStringAsync(key, value.ToJsonString(), _defaultCacheEntryOptions);
    /// <summary>
    /// 移除一个缓存
    /// </summary>
    /// <param name="key">缓存键</param>
    /// <returns></returns>
    public virtual async Task RemoveCacheAsync(string key) => await _cache.RemoveAsync(key);
    /// <summary>
    /// 提交当前操作
    /// </summary>
    /// <returns></returns>
    public virtual Task<int> SaveChangesAsync() => _context.SaveChangesAsync();
    /// <summary>
    /// 设置当前上下文是否自动提交
    /// </summary>
    /// <param name="autoSaveChanges"></param>
    public virtual void SetAutoSaveChanges(bool autoSaveChanges) => AutoSaveChanges = autoSaveChanges;
    /// <summary>
    /// 设置当前数据隔离的编号
    /// </summary>
    /// <param name="dataIsolate">数据隔离级别</param>
    /// <param name="dataIsolateId">数据隔离编号</param>
    public virtual void SetDataIsolate(DataIsolateLevel dataIsolate = DataIsolateLevel.None, TKey? dataIsolateId = default) => (DataIsolate, DataIsolateId) = (dataIsolate, dataIsolateId);
    /// <summary>
    /// 设置超时时间
    /// 超时时间为空则为恢复原本超时时间
    /// </summary>
    /// <param name="timeout">超时时间</param>
    public virtual void SetTimeout(int? timeout = default)
    {
        if (timeout.HasValue)
        {
            _timeout = _context.Database.GetCommandTimeout();
            _context.Database.SetCommandTimeout(timeout);
        }
        else
        {
            _context.Database.SetCommandTimeout(_timeout);
        }
    }
    #endregion

    #region 基础数据操作方法, 面向当前业务类型
    /// <summary>
    /// 获取 TypeAdapterConfig 默认实例
    /// </summary>
    /// <returns></returns>
    public virtual TypeAdapterConfig GetAdapterConfig()
    {
        var config = new TypeAdapterConfig();
        var typeConfig = config.ForDestinationType<TEntitySet>()
            //.PreserveReference(true)
            //.ShallowCopyForSameType(true)
            .AvoidInlineMapping(true)
            //.IgnoreAttribute(typeof(NotMappedAttribute))
            //.Ignore(nameof(Personal.Additionals))
            .MaxDepth(2);

        if (_type.IsFromType<IEntitySetWithCreate<TKey>>())
        {
            typeConfig.Ignore(nameof(IEntitySetWithCreate<TKey>.RowVersion), nameof(IEntitySetWithCreate<TKey>.CreatedBy), nameof(IEntitySetWithCreate<TKey>.CreatedOn));
        }

        if (_type.IsFromType<IEntitySetWithCreateAndUpdate<TKey>>())
        {
            typeConfig.Ignore(nameof(IEntitySetWithCreateAndUpdate<TKey>.UpdatedBy), nameof(IEntitySetWithCreateAndUpdate<TKey>.UpdatedOn));
        }

        return config;
    }
    /// <summary>
    /// 调用实例的 Create 方法为基础字段赋值
    /// </summary>
    /// <param name="entitySet">业务数据</param>
    public virtual void Create(TEntitySet entitySet)
    {
        if (entitySet is IEntitySetWithCreate<TKey> entitySetWithCreate)
        {
            entitySetWithCreate.Create(UserName, _httpSetting.DateTimeKind);
        }

        if (_httpSetting.DataIsolate != DataIsolateLevel.None && DataIsolateId is not null && entitySet is IDataIsolate<TKey> dataIsolate)
        {
            dataIsolate.IsolateId = DataIsolateId;
        }
    }
    /// <summary>
    /// 调用实例的 Update 方法为基础字段赋值
    /// </summary>
    /// <param name="entitySet">业务数据</param>
    public virtual void Update(TEntitySet entitySet)
    {
        if (entitySet is IEntitySetWithCreateAndUpdate<TKey> entitySetWithCreateAndUpdate)
        {
            entitySetWithCreateAndUpdate.Update(UserName, _httpSetting.DateTimeKind);
        }
    }
    /// <summary>
    /// 逻辑修改, 逻辑修改操作会在源数据基础上产生新的记录作为历史记录
    /// </summary>
    /// <param name="entitySet">业务数据</param>
    /// <param name="isLogical">是否逻辑操作, 默认不是</param>
    public virtual void Update(TEntitySet entitySet, bool isLogical = false)
    {
        if (isLogical && entitySet is IEntitySetWithCreate<TKey>)
        {
            //逻辑修改会产生一个当前数据不同主键的副本, 并设置为已更新的状态, 使用 RowVersion 字段关联更新后的数据编号
            var logicalCopy = entitySet.Copy();
            logicalCopy.Id = KeyGenerator.GetKey<TKey>();
            if (logicalCopy is IEntitySetWithCreate<TKey> entitySetWithCreate)
            {
                entitySetWithCreate.RowVersion = entitySet.Id;
                entitySetWithCreate.State = DataState.Updated;
            }

            _dbSet.Add(logicalCopy);
        }
    }
    /// <summary>
    /// 逻辑删除, 逻辑删除操作实际上只是将数据状态修改为已删除状态, 并不会真的删除数据
    /// </summary>
    /// <param name="entitySet">业务数据</param>
    /// <param name="isLogical">是否逻辑操作, 默认不是</param>
    /// <returns></returns>
    public virtual void Delete(TEntitySet entitySet, bool isLogical = false)
    {
        if (isLogical && entitySet is IEntitySetWithCreate<TKey> entitySetWithCreate)
        {
            Update(entitySetWithCreate);
            entitySetWithCreate.State = DataState.Deleted;
            _dbSet.Update(entitySet);
        }
        else
        {
            _dbSet.Remove(entitySet);
        }
    }
    #endregion

    #region 数据查询方法, 面向当前业务类型
    /// <summary>
    /// 统一数据源查询入口, 可缓存数据源, 仅返回当前类型引用
    /// </summary>
    /// <returns></returns>
    public virtual IQueryable<TEntitySet> GetQueryable()
    {
        if (EnableCache)
        {
            var cacheKey = CACHE_DATA + _type.Name.ToUpper(CultureInfo.CurrentCulture);
            var entitySets = _cache.GetValue<IEnumerable<TEntitySet>>(cacheKey);
            if (entitySets is null)
            {
                _logger.LogInformation($"{_userAccess}: query {_typeName} have no cache data, will query and cached");
                entitySets = [.. _dbSet];
                _cache.SetValue(cacheKey, entitySets);
            }

            _logger.LogDebug($"{_userAccess}: query {_typeName} from cache");
            if (DataIsolate != DataIsolateLevel.None && DataIsolateId is not null && entitySets is IEnumerable<IDataIsolate<TKey>> entities)
            {
                _logger.LogDebug($"{_userAccess}: query {_typeName} from cache in data isolate: {DataIsolateId}");
                return entities.Where(x => x.IsolateId.Equals(DataIsolateId)).Cast<TEntitySet>().AsQueryable();
            }

            return entitySets.AsQueryable();
        }

        _logger.LogDebug($"{_userAccess}: query {_typeName}");
        if (DataIsolate != DataIsolateLevel.None && DataIsolateId is not null && _dbSet is IQueryable<IDataIsolate<TKey>> dbset)
        {
            _logger.LogDebug($"{_userAccess}: query {_typeName} in data isolate: {DataIsolateId}");
            return dbset.Where(x => x.IsolateId.Equals(DataIsolateId)).Cast<TEntitySet>().AsQueryable();
        }

        return _dbSet;
    }
    /// <summary>
    /// 非注入类型 TEntity 统一数据源查询入口, 可缓存数据源, 可返回泛型类型指定的任意类型引用
    /// </summary>
    /// <param name="predicate">过滤条件</param>
    /// <returns></returns>
    public virtual IQueryable<TEntitySet> GetQueryable(Expression<Func<TEntitySet, bool>> predicate) => GetQueryable().Where(predicate);
    /// <summary>
    /// 根据条件查询第一条
    /// </summary>
    /// <param name="predicate">过滤条件</param>
    /// <returns></returns>
    public virtual async Task<TEntitySet?> FirstOrDefaultAsync(Expression<Func<TEntitySet, bool>> predicate) => await GetQueryable().FirstOrDefaultAsync(predicate);
    /// <summary>
    /// 根据条件查询是否存在
    /// </summary>
    /// <param name="predicate">过滤条件</param>
    /// <returns></returns>
    public virtual async Task<bool> AnyAsync(Expression<Func<TEntitySet, bool>> predicate) => await GetQueryable().AnyAsync(predicate);
    /// <summary>
    /// 根据 id 获取对象
    /// </summary>
    /// <param name="id">业务数据编号</param>
    /// <returns></returns>
    public virtual async Task<TEntitySet?> GetAsync(TKey id) => await GetQueryable().FirstOrDefaultAsync(x => x.Id.Equals(id));
    /// <summary>
    /// TODO TBD 此方法核心功能尚未经过完整测试
    /// 从 QueryOptions 和上下文自动获取查询表达式的内容并执行查询返回分页的结果
    /// 暂不支持 orderby, select 和 expand 表达式
    /// </summary>
    /// <returns></returns>
    public virtual async Task<PagedResult<TEntitySet>> GetPagedResultAsync() => await _request.GetPagedResultAsync(GetQueryable());
    /// <summary>
    /// 适用于更新时判断当前对象是否存在, 存在则返回按 Id 查询的结果, 不存在则抛出异常
    /// </summary>
    /// <param name="id">业务数据主键</param>
    /// <returns></returns>
    /// <exception cref="BadHttpRequestException"></exception>
    public virtual async Task<TEntitySet> GetIfExistAsync(TKey id) => (await GetAsync(id)) ?? throw new ArgumentNullException($"{_typeName} 中 id 等于 {id} 的记录不存在!");
    /// <summary>
    /// 适用于新增时判断对象是否存在, 存在则抛出异常
    /// </summary>
    /// <param name="id">业务数据编号</param>
    /// <returns></returns>
    /// <exception cref="BadHttpRequestException"></exception>
    public virtual async Task ThrowIfExistAsync(TKey id)
    {
        if (id.Equals(default))
        {
            return;
        }

        if (await AnyAsync(x => x.Id.Equals(id)))
        {
            throw new BadHttpRequestException($"{_typeName} 中当前记录已存在!");
        }
    }
    /// <summary>
    /// 适用于删除时判断对象是否存在, 不存在则抛出异常
    /// </summary>
    /// <param name="id">业务数据编号</param>
    /// <returns></returns>
    /// <exception cref="BadHttpRequestException"></exception>
    public virtual async Task ThrowIfNotExistAsync(TKey id)
    {
        if (id.Equals(default))
        {
            throw new BadHttpRequestException($"{_typeName} 中当前记录编号不可为空!");
        }

        if (!await AnyAsync(x => x.Id.Equals(id)))
        {
            throw new BadHttpRequestException($"{_typeName} 中当前记录不存在!");
        }
    }
    /// <summary>
    /// 从缓存/数据库获取 TEntity 类型的 TResult 类型的属性/字段 memberName 的下一个值
    /// maxLength 用于约束最大长度和截取非数字部分, 如果不传递, 则默认取当前编号的长度
    /// 如: 字段 Code, 构成为: NX+8位数字, 则 maxLength = 8, 会截取后 8 位数字
    /// </summary>
    /// <typeparam name="TResult">字段/属性类型</typeparam>
    /// <param name="memberSelecter">选择字段/属性名称</param>
    /// <param name="maxLength">编码纯数字部分最大长度, 非必须, 默认为字段最后一个值的长度</param>
    /// <param name="prefix">非数字部分前缀, 非必须, 默认为空, 或字段最后一个值除去 maxLength 的前半部分</param>
    /// <param name="maxInterval">最大随机跳过编号, 跳过编号, 会使生成的编号不连续, 防止被盲猜. 非必须, 默认为: 1, 生成连续的编号</param>
    /// <returns></returns>
    /// <exception cref="Exception"></exception>
    public async Task<TResult> GetNextCodeAsync<TResult>(Expression<Func<TEntitySet, TResult>> memberSelecter, int? maxLength = default, string? prefix = default, int maxInterval = UNIT_ONE) => await _context.GetNextCodeAsync<TDbContext, TEntitySet, TResult>(_cache, memberSelecter, maxLength, prefix, maxInterval);
    /// <summary>
    /// 从缓存/数据库获取 TEntity 类型的 TResult 类型的属性/字段 memberName 的下一个值
    /// maxLength 用于约束最大长度和截取非数字部分, 如果不传递, 则默认取当前编号的长度
    /// 如: 字段 Code, 构成为: NX+8位数字, 则 maxLength = 8, 会截取后 8 位数字
    /// </summary>
    /// <param name="memberSelecter">选择字段/属性名称</param>
    /// <param name="maxLength">编码纯数字部分最大长度, 非必须, 默认为字段最后一个值的长度</param>
    /// <param name="prefix">非数字部分前缀, 非必须, 默认为空, 或字段最后一个值除去 maxLength 的前半部分</param>
    /// <param name="maxInterval">最大随机跳过编号, 跳过编号, 会使生成的编号不连续, 防止被盲猜. 非必须, 默认为: 1, 生成连续的编号</param>
    /// <returns></returns>
    /// <exception cref="Exception"></exception>
    public async Task<string> GetNextCodeAsync(Expression<Func<TEntitySet, string>> memberSelecter, int? maxLength = default, string? prefix = default, int maxInterval = UNIT_ONE) => await GetNextCodeAsync<string>(memberSelecter, maxLength, prefix, maxInterval);
    #endregion

    #region 数据查询方法, 面向非当前业务类型, 仅适用于主业务数据关联查询的业务场景, 没有当前主业务数据处理的性能好
    /// <summary>
    /// 统一数据源查询入口, 可缓存数据源, 仅返回当前类型引用
    /// </summary>
    /// <typeparam name="T">非当前业务的数据类型</typeparam>
    /// <returns></returns>
    public virtual IQueryable<T> GetQueryable<T>() where T : class, IEntitySet<TKey>
    {
        var type = typeof(T);
        var typeName = type.GetDisplayName();
        if (_httpSetting.Cache is not null && type.HasAttribute<CacheableAttribute>())
        {
            var cacheKey = CACHE_DATA + type.Name.ToUpper(CultureInfo.CurrentCulture);
            var entitySets = _cache.GetValue<IEnumerable<T>>(cacheKey);
            if (entitySets is null)
            {
                _logger.LogInformation($"{_userAccess}: query {typeName} have no cache data, will query and cached");
                entitySets = [.. _context.Set<T>()];
                _cache.SetValue(cacheKey, entitySets);
            }

            _logger.LogDebug($"{_userAccess}: query {typeName} from cache");
            if (DataIsolate != DataIsolateLevel.None && DataIsolateId is not null && entitySets is IEnumerable<IDataIsolate<TKey>> entities)
            {
                _logger.LogDebug($"{_userAccess}: query {typeName} from cache in data isolate: {DataIsolateId}");
                return entities.Where(x => x.IsolateId.Equals(DataIsolateId)).Cast<T>().AsQueryable();
            }

            return entitySets.AsQueryable();
        }

        _logger.LogDebug($"{_userAccess}: query {typeName}");
        if (DataIsolate != DataIsolateLevel.None && DataIsolateId is not null && _dbSet is IQueryable<IDataIsolate<TKey>> dbset)
        {
            _logger.LogDebug($"{_userAccess}: query {typeName} in data isolate: {DataIsolateId}");
            return dbset.Where(x => x.IsolateId.Equals(DataIsolateId)).Cast<T>().AsQueryable();
        }

        return _context.Set<T>();
    }
    /// <summary>
    /// 非注入类型 TEntity 统一数据源查询入口, 可缓存数据源, 可返回泛型类型指定的任意类型引用
    /// </summary>
    /// <typeparam name="T">非当前业务的数据类型</typeparam>
    /// <param name="predicate">过滤条件</param>
    /// <returns></returns>
    public virtual IQueryable<T> GetQueryable<T>(Expression<Func<T, bool>> predicate) where T : class, IEntitySet<TKey> => GetQueryable<T>().Where(predicate);
    /// <summary>
    /// 根据条件查询第一条
    /// </summary>
    /// <typeparam name="T">非当前业务的数据类型</typeparam>
    /// <param name="predicate">过滤条件</param>
    /// <returns></returns>
    public virtual async Task<T?> FirstOrDefaultAsync<T>(Expression<Func<T, bool>> predicate) where T : class, IEntitySet<TKey> => await GetQueryable<T>().FirstOrDefaultAsync(predicate);
    /// <summary>
    /// 根据条件查询是否存在
    /// </summary>
    /// <typeparam name="T">非当前业务的数据类型</typeparam>
    /// <param name="predicate">过滤条件</param>
    /// <returns></returns>
    public virtual async Task<bool> AnyAsync<T>(Expression<Func<T, bool>> predicate) where T : class, IEntitySet<TKey> => await GetQueryable<T>().AnyAsync(predicate);
    /// <summary>
    /// 根据 id 获取对象
    /// </summary>
    /// <typeparam name="T">非当前业务的数据类型</typeparam>
    /// <param name="id">业务数据主键</param>
    /// <returns></returns>
    public virtual async Task<T?> GetAsync<T>(TKey id) where T : class, IEntitySet<TKey> => await GetQueryable<T>().FirstOrDefaultAsync(x => x.Id.Equals(id));
    /// <summary>
    /// TODO TBD 此方法核心功能尚未经过完整测试
    /// 从 QueryOptions 和上下文自动获取查询表达式的内容并执行查询返回分页的结果
    /// 暂不支持 orderby, select 和 expand 表达式
    /// </summary>
    /// <typeparam name="T">非当前业务的数据类型</typeparam>
    /// <returns></returns>
    public virtual async Task<PagedResult<T>> GetPagedResultAsync<T>() where T : class, IEntitySet<TKey>, new() => await _request.GetPagedResultAsync(GetQueryable<T>());
    /// <summary>
    /// 适用于更新时判断当前对象是否存在, 存在则返回按 Id 查询的结果, 不存在则抛出异常
    /// </summary>
    /// <typeparam name="T">非当前业务的数据类型</typeparam>
    /// <param name="id">业务数据主键</param>
    /// <returns></returns>
    /// <exception cref="BadHttpRequestException"></exception>
    public virtual async Task<T> GetIfExistAsync<T>(TKey id) where T : class, IEntitySet<TKey> => (await GetAsync<T>(id)) ?? throw new BadHttpRequestException($"{typeof(T).GetDisplayName()} 中 id 等于 {id} 的记录不存在!");
    /// <summary>
    /// 适用于新增时判断对象是否存在, 存在则抛出异常
    /// </summary>
    /// <typeparam name="T">非当前业务的数据类型</typeparam>
    /// <param name="id">业务数据编号</param>
    /// <returns></returns>
    /// <exception cref="BadHttpRequestException"></exception>
    public virtual async Task ThrowIfExistAsync<T>(TKey id) where T : class, IEntitySet<TKey>
    {
        if (id.Equals(default))
        {
            return;
        }

        if (await AnyAsync<T>(x => x.Id.Equals(id)))
        {
            throw new BadHttpRequestException($"{typeof(T).GetDisplayName()} 中当前记录已存在!");
        }
    }
    /// <summary>
    /// 适用于删除时判断对象是否存在, 不存在则抛出异常
    /// </summary>
    /// <typeparam name="T">非当前业务的数据类型</typeparam>
    /// <param name="id">业务数据编号</param>
    /// <returns></returns>
    /// <exception cref="BadHttpRequestException"></exception>
    public virtual async Task ThrowIfNotExistAsync<T>(TKey id) where T : class, IEntitySet<TKey>
    {
        if (id.Equals(default))
        {
            throw new BadHttpRequestException($"{_typeName} 中当前记录编号不可为空!");
        }

        if (!await AnyAsync<T>(x => x.Id.Equals(id)))
        {
            throw new BadHttpRequestException($"{_typeName} 中当前记录不存在!");
        }
    }
    /// <summary>
    /// 从缓存/数据库获取 TEntity 类型的 TResult 类型的属性/字段 memberName 的下一个值
    /// maxLength 用于约束最大长度和截取非数字部分, 如果不传递, 则默认取当前编号的长度
    /// 如: 字段 Code, 构成为: NX+8位数字, 则 maxLength = 8, 会截取后 8 位数字
    /// </summary>
    /// <typeparam name="T">数据对象模型类型</typeparam>
    /// <typeparam name="TResult">字段/属性类型</typeparam>
    /// <param name="memberSelecter">选择字段/属性名称</param>
    /// <param name="maxLength">编码纯数字部分最大长度, 非必须, 默认为字段最后一个值的长度</param>
    /// <param name="prefix">非数字部分前缀, 非必须, 默认为空, 或字段最后一个值除去 maxLength 的前半部分</param>
    /// <param name="maxInterval">最大随机跳过编号, 跳过编号, 会使生成的编号不连续, 防止被盲猜. 非必须, 默认为: 1, 生成连续的编号</param>
    /// <returns></returns>
    /// <exception cref="Exception"></exception>
    public async Task<TResult> GetNextCodeAsync<T, TResult>(Expression<Func<T, TResult>> memberSelecter, int? maxLength = default, string? prefix = default, int maxInterval = UNIT_ONE) where T : class, IEntitySet<TKey> => await _context.GetNextCodeAsync<TDbContext, T, TResult>(_cache, memberSelecter, maxLength, prefix, maxInterval);
    /// <summary>
    /// 从缓存/数据库获取 TEntity 类型的 TResult 类型的属性/字段 memberName 的下一个值
    /// maxLength 用于约束最大长度和截取非数字部分, 如果不传递, 则默认取当前编号的长度
    /// 如: 字段 Code, 构成为: NX+8位数字, 则 maxLength = 8, 会截取后 8 位数字
    /// </summary>
    /// <typeparam name="T">数据对象模型类型</typeparam>
    /// <param name="memberSelecter">选择字段/属性名称</param>
    /// <param name="maxLength">编码纯数字部分最大长度, 非必须, 默认为字段最后一个值的长度</param>
    /// <param name="prefix">非数字部分前缀, 非必须, 默认为空, 或字段最后一个值除去 maxLength 的前半部分</param>
    /// <param name="maxInterval">最大随机跳过编号, 跳过编号, 会使生成的编号不连续, 防止被盲猜. 非必须, 默认为: 1, 生成连续的编号</param>
    /// <returns></returns>
    /// <exception cref="Exception"></exception>
    public async Task<string> GetNextCodeAsync<T>(Expression<Func<T, string>> memberSelecter, int? maxLength = default, string? prefix = default, int maxInterval = UNIT_ONE) where T : class, IEntitySet<TKey> => await GetNextCodeAsync<T, string>(memberSelecter, maxLength, prefix, maxInterval);
    #endregion

    #region 数据操作方法, 面向当前业务类型
    /// <summary>
    /// 新增阶段判断满足条件的数据是否存在, 存在则抛出异常, 不存在则执行基类预定义赋值操作
    /// </summary>
    /// <param name="entitySet">业务数据</param>
    /// <returns></returns>
    /// <exception cref="BadHttpRequestException"></exception>
    public virtual async Task UniqueAsync(TEntitySet entitySet)
    {
        var propertyInfos = _propertyInfos.Where(x => x.HasAttribute<UniqueAttribute>());
        if (propertyInfos.IsNotNullOrEmpty())
        {
            var index = 0;
            var keys = new List<string>();
            var values = new List<object>();
            var messages = new List<string>();

            if (!(entitySet.Id == null || entitySet.Id.Equals(default)))
            {
                keys.Add($"{nameof(entitySet.Id)} != @{index++}");
                values.Add($"{entitySet.Id}");
            }

            foreach (var propertyInfo in propertyInfos)
            {
                var value = propertyInfo.GetValue(entitySet);
                keys.Add($"{propertyInfo.Name} == @{index++}");
                values.Add($"{value}");
                messages.Add($"<{propertyInfo.GetDisplayName()}> = {value}");
            }

            var predicate = string.Join(" and ", keys);
            var exist = await GetQueryable().AnyAsync(predicate, [.. values]);
            if (exist)
            {
                throw new BadHttpRequestException($"{_typeName} 中 {messages.ToString<string>()} 的记录已经存在!");
            }
        }
    }
    /// <summary>
    /// 单个数据对象的新增, 自动验证重复
    /// </summary>
    /// <param name="entitySet">业务数据</param>
    /// <param name="context">数据操作上下文</param>
    /// <returns></returns>
    public virtual async Task<TEntitySet> AddAsync(TEntitySet entitySet, DataServiceContext? context = default)
    {
        await InternalAddAsync(entitySet, context);
        await InternalSaveChangesAsync(context);
        return entitySet;
    }
    /// <summary>
    /// 更新阶段判断满足条件的数据是否存在, 存在则抛出异常, 不存在则执行基类预定义赋值操作, 自动验证重复
    /// </summary>
    /// <param name="entitySet">业务数据</param>
    /// <param name="context">数据操作上下文</param>
    /// <returns></returns>
    public virtual async Task<TEntitySet> UpdateAsync(TEntitySet entitySet, DataServiceContext? context = default)
    {
        await InternalUpdateAsync(entitySet, context);
        await InternalSaveChangesAsync(context);
        return entitySet;
    }
    /// <summary>
    /// 删除记录
    /// </summary>
    /// <param name="id">业务数据主键</param>
    /// <param name="context">数据操作上下文</param>
    /// <returns></returns>
    public virtual async Task DeleteAsync(TKey id, DataServiceContext? context = default)
    {
        await InternalDeleteAsync(id, context);
        await InternalSaveChangesAsync(context);
    }
    /// <summary>
    /// 删除记录
    /// </summary>
    /// <param name="entitySet">业务数据</param>
    /// <param name="context">数据操作上下文</param>
    /// <returns></returns>
    public virtual async Task DeleteAsync(TEntitySet entitySet, DataServiceContext? context = default)
    {
        await InternalDeleteAsync(entitySet, context);
        await InternalSaveChangesAsync(context);
    }
    #endregion

    #region 批量数据操作方法, 面向当前业务类型
    /// <summary>
    /// 新增阶段判断满足条件的数据是否存在, 存在则抛出异常, 不存在则执行基类预定义赋值操作
    /// </summary>
    /// <param name="entitySets">业务数据集合</param>
    /// <returns></returns>
    /// <exception cref="BadHttpRequestException"></exception>
    public virtual async Task UniquesAsync(IEnumerable<TEntitySet> entitySets)
    {
        var propertyInfos = _propertyInfos.Where(x => x.HasAttribute<UniqueAttribute>());
        if (propertyInfos.IsNotNullOrEmpty())
        {
            var index = 0;
            var argIndex = 0;
            var predicate = string.Empty;
            var exist = false;
            var values = new List<object>();
            var messages = new List<string>();
            var predicates = new List<string>();

            foreach (var entitySet in entitySets)
            {
                var keys = new List<string>();
                if (!(entitySet.Id == null || entitySet.Id.Equals(default)))
                {
                    keys.Add($"{nameof(entitySet.Id)} != @{argIndex++}");
                    values.Add($"{entitySet.Id}");
                }

                foreach (var propertyInfo in propertyInfos)
                {
                    var value = propertyInfo.GetValue(entitySet);
                    keys.Add($"{propertyInfo.Name} == @{argIndex++}");
                    values.Add($"{value}");
                    messages.Add($"<{propertyInfo.GetDisplayName()}> = {value}");
                }

                predicates.Add(string.Join(" and ", keys));

                //预设一个临界值, 当一次查询两超过 100 条时, 拼接的语句过长, 因此每 100 条进行一次断句
                if (index++ >= UNIT_HUNDRED)
                {
                    predicate = "(" + string.Join(") or (", predicates) + ")";
                    exist = await GetQueryable().AnyAsync(predicate, [.. values]);
                    if (exist)
                    {
                        throw new BadHttpRequestException($"{_typeName} 中 {messages.ToString<string>()} 的记录已经存在!");
                    }

                    index = 0;
                    argIndex = 0;
                    values.Clear();
                    messages.Clear();
                    predicates.Clear();
                }
            }

            predicate = "(" + string.Join(") or (", predicates) + ")";
            exist = await GetQueryable().AnyAsync(predicate, [.. values]);
            if (exist)
            {
                throw new BadHttpRequestException($"{_typeName} 中 {messages.ToString<string>()} 的记录已经存在!");
            }
        }
    }
    /// <summary>
    /// 数据对象的新增, 自动验证重复
    /// </summary>
    /// <param name="entitySets">业务数据集合</param>
    /// <param name="context">数据操作上下文</param>
    /// <returns></returns>
    public virtual async Task AddsAsync(IEnumerable<TEntitySet> entitySets, DataServiceContext? context = default)
    {
        await InternalAddsAsync(entitySets, context);
        await InternalSaveChangesAsync(context);
    }
    /// <summary>
    /// 更新阶段判断满足条件的数据是否存在, 存在则抛出异常, 不存在则执行基类预定义赋值操作, 自动验证重复
    /// </summary>
    /// <param name="entitySets">业务数据集合</param>
    /// <param name="context">数据操作上下文</param>
    /// <returns></returns>
    public virtual async Task UpdatesAsync(IEnumerable<TEntitySet> entitySets, DataServiceContext? context = default)
    {
        await InternalUpdatesAsync(entitySets, context);
        await InternalSaveChangesAsync(context);
    }
    /// <summary>
    /// 删除记录
    /// </summary>
    /// <param name="ids">业务数据主键集合</param>
    /// <param name="context">数据操作上下文</param>
    /// <returns></returns>
    public virtual async Task DeletesAsync(IEnumerable<TKey> ids, DataServiceContext? context = default)
    {
        await InternalDeletesAsync(ids, context);
        await InternalSaveChangesAsync(context);
    }
    /// <summary>
    /// 删除记录
    /// </summary>
    /// <param name="entitySets">业务数据集合</param>
    /// <param name="context">数据操作上下文</param>
    /// <returns></returns>
    public virtual async Task DeletesAsync(IEnumerable<TEntitySet> entitySets, DataServiceContext? context = default)
    {
        await InternalDeletesAsync(entitySets, context);
        await InternalSaveChangesAsync(context);
    }
    #endregion

    #region 数据操作方法, 面向当前业务的视图模型类型
    /// <summary>
    /// 单个数据对象的新增, 自动验证重复
    /// </summary>
    /// <typeparam name="TViewModel">业务数据类型的视图模型</typeparam>
    /// <param name="viewModel">业务数据视图模型</param>
    /// <param name="context">数据操作上下文</param>
    /// <returns></returns>
    public virtual async Task<TEntitySet> AddAsync<TViewModel>(TViewModel viewModel, DataServiceContext? context = default) where TViewModel : class, IViewModel<TKey> => await AddAsync(viewModel.Adapt<TEntitySet>(context?.AdapterConfig ?? GetAdapterConfig()), context);
    /// <summary>
    /// 更新阶段判断满足条件的数据是否存在, 存在则抛出异常, 不存在则执行基类预定义赋值操作
    /// </summary>
    /// <typeparam name="TViewModel">业务数据类型的视图模型</typeparam>
    /// <param name="viewModel">业务数据视图模型</param>
    /// <param name="context">数据操作上下文</param>
    /// <returns></returns>
    public virtual async Task<TEntitySet> UpdateAsync<TViewModel>(TViewModel viewModel, DataServiceContext? context = default) where TViewModel : class, IViewModel<TKey> => await UpdateAsync(viewModel.Adapt<TEntitySet>(context?.AdapterConfig ?? GetAdapterConfig()), context);
    #endregion

    #region 批量数据操作方法, 面向当前业务的视图模型类型 TODO TBV
    /// <summary>
    /// 单个数据对象的新增, 自动验证重复
    /// </summary>
    /// <typeparam name="TViewModel">业务数据类型的视图模型</typeparam>
    /// <param name="viewModels">业务数据视图模型集合</param>
    /// <param name="context">数据操作上下文</param>
    /// <returns></returns>
    public virtual async Task AddsAsync<TViewModel>(IEnumerable<TViewModel> viewModels, DataServiceContext? context = default) where TViewModel : class, IViewModel<TKey> => await AddsAsync(viewModels.Adapt<IEnumerable<TEntitySet>>(context?.AdapterConfig ?? GetAdapterConfig()), context);
    /// <summary>
    /// 更新阶段判断满足条件的数据是否存在, 存在则抛出异常, 不存在则执行基类预定义赋值操作
    /// </summary>
    /// <typeparam name="TViewModel">业务数据类型的视图模型</typeparam>
    /// <param name="viewModels">业务数据视图模型集合</param>
    /// <param name="context">数据操作上下文</param>
    /// <returns></returns>
    public virtual async Task UpdatesAsync<TViewModel>(IEnumerable<TViewModel> viewModels, DataServiceContext? context = default) where TViewModel : class, IViewModel<TKey> => await UpdatesAsync(viewModels.Adapt<IEnumerable<TEntitySet>>(context?.AdapterConfig ?? GetAdapterConfig()), context);
    #endregion

    #region 内部成员方法
    #region 基础操作方法
    /// <summary>
    /// 获取 TypeAdapterConfig 默认实例
    /// </summary>
    /// <typeparam name="TModel">业务数据类型</typeparam>
    /// <returns></returns>
    protected virtual TypeAdapterConfig GetAdapterConfig<TModel>() where TModel : class, IEntitySet<TKey>
    {
        var config = new TypeAdapterConfig();
        var typeConfig = config.ForDestinationType<TModel>()
            //.PreserveReference(true)
            //.ShallowCopyForSameType(true)
            .AvoidInlineMapping(true)
            //.IgnoreAttribute(typeof(NotMappedAttribute))
            //.Ignore(nameof(Personal.Additionals))
            .MaxDepth(2);

        var type = typeof(TModel);
        if (type.IsFromType<IEntitySetWithCreate<TKey>>())
        {
            typeConfig.Ignore(nameof(IEntitySetWithCreate<TKey>.RowVersion), nameof(IEntitySetWithCreate<TKey>.CreatedBy), nameof(IEntitySetWithCreate<TKey>.CreatedOn));
        }

        if (type.IsFromType<IEntitySetWithCreateAndUpdate<TKey>>())
        {
            typeConfig.Ignore(nameof(IEntitySetWithCreateAndUpdate<TKey>.UpdatedBy), nameof(IEntitySetWithCreateAndUpdate<TKey>.UpdatedOn));
        }

        return config;
    }
    /// <summary>
    /// 调用实例的 Create 方法为基础字段赋值
    /// </summary>
    /// <typeparam name="TModel">业务数据类型</typeparam>
    /// <param name="model">业务数据</param>
    protected virtual void Create<TModel>(TModel model) where TModel : class, IEntitySet<TKey>
    {
        if (model is IEntitySetWithCreate<TKey> entitySetWithCreate)
        {
            entitySetWithCreate.Create(UserName, _httpSetting.DateTimeKind);
        }

        if (_httpSetting.DataIsolate != DataIsolateLevel.None && DataIsolateId is not null && model is IDataIsolate<TKey> dataIsolate)
        {
            dataIsolate.IsolateId = DataIsolateId;
        }
    }
    /// <summary>
    /// 调用实例的 Update 方法为基础字段赋值
    /// </summary>
    /// <typeparam name="TModel">业务数据类型</typeparam>
    /// <param name="model">业务数据</param>
    protected virtual void Update<TModel>(TModel model) where TModel : class, IEntitySet<TKey>
    {
        if (model is IEntitySetWithCreateAndUpdate<TKey> entitySetWithCreateAndUpdate)
        {
            entitySetWithCreateAndUpdate.Update(UserName, _httpSetting.DateTimeKind);
        }
    }
    /// <summary>
    /// 逻辑修改, 逻辑修改操作会在源数据基础上产生新的记录作为历史记录
    /// </summary>
    /// <typeparam name="TModel">业务数据类型</typeparam>
    /// <param name="model">业务数据</param>
    /// <param name="isLogical">是否逻辑操作, 默认不是</param>
    protected virtual void Update<TModel>(TModel model, bool isLogical = false) where TModel : class, IEntitySet<TKey>
    {
        if (isLogical && model is IEntitySetWithCreate<TKey>)
        {
            //逻辑修改会产生一个当前数据不同主键的副本, 并设置为已更新的状态, 使用 RowVersion 字段关联更新后的数据编号
            var logicalCopy = model.Copy();
            logicalCopy.Id = KeyGenerator.GetKey<TKey>();
            if (logicalCopy is IEntitySetWithCreate<TKey> entitySetWithCreate)
            {
                entitySetWithCreate.RowVersion = model.Id;
                entitySetWithCreate.State = DataState.Updated;
            }

            _context.Set<TModel>().Add(logicalCopy);
        }
    }
    /// <summary>
    /// 逻辑删除, 逻辑删除操作实际上只是将数据状态修改为已删除状态, 并不会真的删除数据
    /// </summary>
    /// <typeparam name="TModel">业务数据类型</typeparam>
    /// <param name="model">业务数据</param>
    /// <param name="isLogical">是否逻辑操作, 默认不是</param>
    /// <returns></returns>
    protected virtual void Delete<TModel>(TModel model, bool isLogical = false) where TModel : class, IEntitySet<TKey>
    {
        if (isLogical && model is IEntitySetWithCreate<TKey> entitySetWithCreate)
        {
            Update(entitySetWithCreate);
            entitySetWithCreate.State = DataState.Deleted;
            _context.Set<TModel>().Update(model);
        }
        else
        {
            _context.Set<TModel>().Remove(model);
        }
    }
    #endregion

    #region 数据操作核心逻辑
    /// <summary>
    /// 内部执行的新增逻辑
    /// </summary>
    /// <param name="entitySet">业务数据</param>
    /// <param name="context">数据操作上下文</param>
    /// <returns></returns>
    protected virtual async Task InternalAddAsync(TEntitySet entitySet, DataServiceContext? context = default)
    {
        var isLogical = context?.IsLogical ?? false;
        var isUnique = context?.IsUnique ?? true;
        _logger.LogInformation($"{_userAccess}: will {GetLogicalString(isLogical)} add {_typeName}, the content is: {entitySet.ToJsonString()}");

        await ThrowIfExistAsync(entitySet.Id);
        if (!isLogical && isUnique)
        {
            //逻辑新增不在校验字段唯一性, 因为逻辑新增状态实为复制数据
            await UniqueAsync(entitySet);
        }

        Create(entitySet);
        Update(entitySet);
        //Detach(entitySet);
        _dbSet.Add(entitySet);
    }
    /// <summary>
    /// 内部执行的更新逻辑
    /// </summary>
    /// <param name="entitySet">业务数据</param>
    /// <param name="context">数据操作上下文</param>
    /// <returns></returns>
    protected virtual async Task InternalUpdateAsync(TEntitySet entitySet, DataServiceContext? context = default)
    {
        var isLogical = context?.IsLogical ?? false;
        var isUnique = context?.IsUnique ?? true;
        _logger.LogInformation($"{_userAccess}: will {GetLogicalString(isLogical)} update {_typeName}, the content is: {entitySet.ToJsonString()}");

        if (!isLogical && isUnique)
        {
            //逻辑更新不在校验字段唯一性, 因为逻辑更新的数据可能为原数据复制品
            await UniqueAsync(entitySet);
        }

        var entity = await GetIfExistAsync(entitySet.Id);
        _logger.LogInformation($"{_userAccess}: will {GetLogicalString(isLogical)} update {_typeName}, the orginal content is: {entity.ToJsonString()}");

        //先对原数据进行逻辑更新操作
        Update(entity, isLogical);

        //修改时先查了一次当前对象, 因此不能取消附加
        //Detach(entity);
        entitySet.Adapt(entity, context?.AdapterConfig ?? GetAdapterConfig());
        Update(entity);
        _dbSet.Update(entity);
    }
    /// <summary>
    /// 内部执行的删除逻辑
    /// </summary>
    /// <param name="id">业务数据主键</param>
    /// <param name="context">数据操作上下文</param>
    /// <returns></returns>
    protected virtual async Task InternalDeleteAsync(TKey id, DataServiceContext? context = default)
    {
        var entitySet = await GetIfExistAsync(id);
        var isLogical = context?.IsLogical ?? false;
        _logger.LogInformation($"{_userAccess}: will {GetLogicalString(isLogical)} delete {_typeName}, the content is: {entitySet.ToJsonString()}");

        Delete(entitySet, isLogical);
    }
    /// <summary>
    /// 内部执行的删除逻辑
    /// </summary>
    /// <param name="entitySet">业务数据</param>
    /// <param name="context">数据操作上下文</param>
    /// <returns></returns>
    protected virtual async Task InternalDeleteAsync(TEntitySet entitySet, DataServiceContext? context = default)
    {
        await ThrowIfNotExistAsync(entitySet.Id);
        var isLogical = context?.IsLogical ?? false;
        _logger.LogInformation($"{_userAccess}: will {GetLogicalString(isLogical)} delete {_typeName}, the content is: {entitySet.ToJsonString()}");

        Delete(entitySet, isLogical);
    }
    #endregion

    #region 批量数据操作核心逻辑
    /// <summary>
    /// 内部执行的新增逻辑
    /// </summary>
    /// <param name="entitySets">业务数据集合</param>
    /// <param name="context">数据操作上下文</param>
    /// <returns></returns>
    protected virtual async Task InternalAddsAsync(IEnumerable<TEntitySet> entitySets, DataServiceContext? context = default)
    {
        var isLogical = context?.IsLogical ?? false;
        var isUnique = context?.IsUnique ?? true;
        _logger.LogInformation($"{_userAccess}: will {GetLogicalString(isLogical)} adds {_typeName}, the content is: {entitySets.ToJsonString()}");

        if (await AnyAsync(x => entitySets.Select(a => a.Id).Contains(x.Id)))
        {
            throw new BadHttpRequestException($"{_typeName} 中当前记录已存在!");
        }

        if (!isLogical && isUnique)
        {
            //逻辑更新不在校验字段唯一性, 因为逻辑更新的数据可能为原数据复制品
            await UniquesAsync(entitySets);
        }

        foreach (var entitySet in entitySets)
        {
            Create(entitySet);
            Update(entitySet);
            //Detach(entitySet);
            _dbSet.Add(entitySet);
        }

        //经验证, 循环 Add 和一次性 AddRange, efcore 在执行时, 过程是一样的
        //await _dbSet.AddRangeAsync(entitySets);
    }
    /// <summary>
    /// 内部执行的更新逻辑
    /// </summary>
    /// <param name="entitySets">业务数据集合</param>
    /// <param name="context">数据操作上下文</param>
    /// <returns></returns>
    protected virtual async Task InternalUpdatesAsync(IEnumerable<TEntitySet> entitySets, DataServiceContext? context = default)
    {
        var isLogical = context?.IsLogical ?? false;
        var isUnique = context?.IsUnique ?? true;
        _logger.LogInformation($"{_userAccess}: will {GetLogicalString(isLogical)} updates {_typeName}, the content is: {entitySets.ToJsonString()}");

        if (!isLogical && isUnique)
        {
            //逻辑更新不在校验字段唯一性, 因为逻辑更新的数据可能为原数据复制品
            await UniquesAsync(entitySets);
        }

        var adds = entitySets.Where(x => x.Id is null).ToList();
        if (adds is not null && adds.Count != 0)
        {
            //没有主键的部分进行批量新增操作
            await InternalAddsAsync(adds, context);
        }

        var updates = entitySets.Where(x => x.Id is not null).ToList();
        if (updates is not null && updates.Count != 0)
        {
            //有主键的部分进行批量更新操作
            var entities = await _dbSet.Where(x => updates.Select(a => a.Id).Contains(x.Id)).ToListAsync();
            if (entities is not null && entities.Count != 0)
            {
                foreach (var entity in entities)
                {
                    //先对原数据进行逻辑更新操作
                    Update(entity, isLogical);

                    var entitySet = updates.FirstOrDefault(x => x.Id.Equals(entity.Id));
                    if (entitySet is not null)
                    {
                        //修改时先查了一次当前对象, 因此不能取消附加
                        //Detach(entity);
                        entitySet.Adapt(entity, context?.AdapterConfig ?? GetAdapterConfig());
                        Update(entity);
                        _dbSet.Update(entity);
                    }
                }
            }
        }
    }
    /// <summary>
    /// 内部执行的删除逻辑
    /// </summary>
    /// <param name="ids">业务数据主键集合</param>
    /// <param name="context">数据操作上下文</param>
    /// <returns></returns>
    protected virtual async Task InternalDeletesAsync(IEnumerable<TKey> ids, DataServiceContext? context = default)
    {
        if (ids.Any())
        {
            if (!await AnyAsync(x => ids.Contains(x.Id)))
            {
                throw new BadHttpRequestException($"{_typeName} 中当前记录不存在!");
            }

            var entitySets = await _dbSet.Where(x => ids.Contains(x.Id)).ToListAsync();
            if (entitySets is not null && entitySets.Count != 0)
            {
                var isLogical = context?.IsLogical ?? false;
                _logger.LogInformation($"{_userAccess}: will {GetLogicalString(isLogical)} delete {_typeName}, the content is: {entitySets.ToJsonString()}");
                foreach (var entitySet in entitySets)
                {
                    Delete(entitySet, isLogical);
                }
            }
        }
    }
    /// <summary>
    /// 内部执行的删除逻辑
    /// </summary>
    /// <param name="entitySets">业务数据集合</param>
    /// <param name="context">数据操作上下文</param>
    /// <returns></returns>
    protected virtual async Task InternalDeletesAsync(IEnumerable<TEntitySet> entitySets, DataServiceContext? context = default)
    {
        if (entitySets is not null && entitySets.Any())
        {
            if (!await AnyAsync(x => entitySets.Select(a => a.Id).Contains(x.Id)))
            {
                throw new BadHttpRequestException($"{_typeName} 中当前记录不存在!");
            }

            var isLogical = context?.IsLogical ?? false;
            _logger.LogInformation($"{_userAccess}: will {GetLogicalString(isLogical)} delete {_typeName}, the content is: {entitySets.ToJsonString()}");
            foreach (var entitySet in entitySets)
            {
                Delete(entitySet, isLogical);
            }
        }
    }
    #endregion

    #region 其他方法
    /// <summary>
    /// 提交当前操作的内部包装方法, 包含判断是否自动提交
    /// </summary>
    /// <param name="context">数据操作上下文</param>
    /// <returns></returns>
    protected virtual async Task<int> InternalSaveChangesAsync(DataServiceContext? context = default)
    {
        context?.Before?.Invoke();
        if (context?.AutoSaveChanges ?? AutoSaveChanges ?? true)
        {
            var result = await SaveChangesAsync();
            if (result > 0)
            {
                _logger.LogInformation($"{_userAccess}: save change {_typeName} to database success {GetLogicalString(context?.IsLogical ?? false)}");
                context?.After?.Invoke();
            }

            return result;
        }

        return 0;
    }
    /// <summary>
    /// 获取是否逻辑操作的字符串
    /// </summary>
    /// <param name="isLogical"></param>
    /// <returns></returns>
    protected virtual string GetLogicalString(bool isLogical = false) => isLogical ? "logical" : "physical";
    #endregion
    #endregion
}

/// <summary>
/// 公共数据处理服务
/// 字符串作为键的默认实现
/// </summary>
/// <typeparam name="TDbContext">数据库上下文</typeparam>
/// <typeparam name="TEntitySet">业务数据类型</typeparam>
public class DataService<TDbContext, TEntitySet>(
    ILogger<DataService<TDbContext, TEntitySet>> logger,
    TDbContext context,
    IDistributedCache cache,
    IHttpContextAccessor httpContextAccessor,
    HttpSetting httpSetting) :
    DataService<TDbContext, TEntitySet, string>(logger, context, cache, httpContextAccessor, httpSetting),
    IDataService<TDbContext, TEntitySet>
    where TDbContext : DbContext
    where TEntitySet : class, IEntitySet, new();